package com.kyrie.system.mybatis.service.impl;

import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.kyrie.system.mybatis.dao.StoredProcedureHandler;
import com.kyrie.system.mybatis.service.IDefaultService;
import com.kyrie.system.mybatis.execption.ServiceException;
import org.apache.commons.lang3.time.FastDateFormat;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ParameterizedPreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.StoredProcedure;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/***
 * @author wuxiang
 * @date 2020-04-06
 * 封装mybatis数据库通用方法
 */
@Service("defaultService")
public class DefaultService implements IDefaultService {
    @Resource(name = "sqlSession")
    private SqlSession session;

    @Autowired
    JdbcTemplate jdbcTemplate;

    public static final int BATCH_SIZE = 5000;

    private IDefaultService fallback = null;

    private List<Class<Throwable>> fallbackExceptions = null;

    public static final long INITVAL_12 = 100000000000L;

    public static final long INITVAL_13 = 1000000000000L;

    public static final long INITVAL_7 = 1000000L;

    public static final long INITVAL_8 = 10000000L;

    /*public void setJdbcTemplate(@Autowired JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }*/

    @Override
    public int insert(String statementName, Object param)
            throws ServiceException {
        try {
            return this.session.insert(statementName, param);
        } catch (Exception e) {
            throw new ServiceException(" 插入失败", e);
        }
    }

    @Override
    public void batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes) throws ServiceException {
        try {
            int fromIndex = 0;
            int toIndex = 5000;
            while (fromIndex != batchArgs.size()) {
                if (toIndex > batchArgs.size()) {
                    toIndex = batchArgs.size();
                }
                this.jdbcTemplate.batchUpdate(sql, batchArgs.subList(fromIndex, toIndex), argTypes);
                fromIndex = toIndex;
                toIndex += 5000;
                if (toIndex > batchArgs.size()) {
                    toIndex = batchArgs.size();
                }
            }
        } catch (Exception e) {
            throw new ServiceException(" 批量更新失败", e);
        }
    }

    @Override
    public int delete(String statementName, Object param) throws ServiceException {
        try {
            return this.session.delete(statementName, param);
        } catch (Exception e) {
            throw new ServiceException(" 删除失败", e);
        }
    }

    @Override
    public int update(String statementName, Object param) throws ServiceException {
        try {
            return this.session.update(statementName, param);
        } catch (Exception e) {
            throw new ServiceException(" 更新失败", e);
        }
    }

    @Override
    public <E> List<E> selectList(String statementName) throws ServiceException {
        try {
            return this.session.selectList(statementName);
        } catch (Exception e) {
            if (this.fallbackExceptions != null) {
                for (Class<Throwable> c : this.fallbackExceptions) {
                    if (c.isAssignableFrom(e.getClass())) {
                        return this.fallback.selectList(statementName);
                    }
                }
            }
            throw new ServiceException(" 查询失败", e);
        }
    }
    @Override
    public List<Map<String, Object>> queryList(String sql, List<Object> args, int[] types) {
        try {
            return this.jdbcTemplate.queryForList(sql, args != null ? args.toArray() : null, types);
        } catch (DataAccessException dex) {
            throw new ServiceException(" 查询失败", dex);
        }
    }
    @Override
    public <T> List<T> queryList(String sql, List<Object> args, int[] types, Class<T> clazz) {
        try {
            return this.jdbcTemplate.queryForList(sql, args != null ? args.toArray() : null, types, clazz);
        } catch (DataAccessException dex) {
            throw new ServiceException(" 查询失败", dex);
        }
    }
    @Override
    public List<Map<String, Object>> queryList(String sql) {
        try {
            return this.jdbcTemplate.queryForList(sql);
        } catch (DataAccessException dex) {
            throw new ServiceException(" 查询失败", dex);
        }
    }
    @Override
    public <T> List<T> queryList(String sql, Class<T> clazz) {
        try {
            return this.jdbcTemplate.queryForList(sql, clazz);
        } catch (DataAccessException dex) {
            throw new ServiceException(" 查询失败", dex);
        }
    }
    @Override
    public List<Map<String, Object>> queryList(String sql, Object... args) {
        try {
            return this.jdbcTemplate.queryForList(sql, args);
        } catch (DataAccessException dex) {
            throw new ServiceException("查询失败", dex);
        }
    }
    @Override
    public <T> List<T> queryList(String sql, Class<T> clazz, Object... args) {
        try {
            return this.jdbcTemplate.queryForList(sql, clazz, args);
        } catch (DataAccessException dex) {
            throw new ServiceException("查询失败", dex);
        }
    }
    @Override
    public <T> List<T> queryList(String sql, List<Object> args, Class<T> clazz) {
        try {
            return this.jdbcTemplate.queryForList(sql, args != null ? args.toArray() : null, clazz);
        } catch (DataAccessException dex) {
            throw new ServiceException("查询失败", dex);
        }
    }
    @Override
    public <T> List<T> queryList(String sql, List<Object> args, RowMapper<T> rowMapper)
            throws ServiceException {
        try {
            return this.jdbcTemplate.query(sql, args != null ? args.toArray() : null, rowMapper);
        } catch (DataAccessException dex) {
            throw new ServiceException("查询失败", dex);
        }
    }
    @Override
    public <T> T queryOneReocrd(String sql, Class<T> clazz) {
        try {
            return (T) this.jdbcTemplate.queryForObject(sql, clazz);
        } catch (EmptyResultDataAccessException dex) {
            return null;
        } catch (DataAccessException dex) {
            throw new ServiceException("查询失败", dex);
        }
    }
    @Override
    public <T> T queryOneReocrd(String sql, RowMapper<T> rowMapper) {
        try {
            return (T) this.jdbcTemplate.queryForObject(sql, rowMapper);
        } catch (EmptyResultDataAccessException dex) {
            return null;
        } catch (DataAccessException dex) {
            throw new ServiceException("查询失败", dex);
        }
    }
    @Override
    public <T> T queryOneReocrd(String sql, List<Object> args, Class<T> clazz) {
        try {
            return (T) this.jdbcTemplate.queryForObject(sql, args != null ? args.toArray() : null, clazz);
        } catch (EmptyResultDataAccessException dex) {
            return null;
        } catch (DataAccessException dex) {
            throw new ServiceException("查询失败", dex);
        }
    }
    @Override
    public <T> T queryOneReocrd(String sql, List<Object> args, RowMapper<T> rowMapper) {
        try {
            return (T) this.jdbcTemplate.queryForObject(sql, args != null ? args.toArray() : null, rowMapper);
        } catch (EmptyResultDataAccessException dex) {
            return null;
        } catch (DataAccessException dex) {
            throw new ServiceException("查询失败", dex);
        }
    }
    @Override
    public <T> T queryOneReocrd(String sql, List<Object> args, int[] argTypes, Class<T> clazz) {
        try {
            return (T) this.jdbcTemplate.queryForObject(sql, args != null ? args.toArray() : null, argTypes, clazz);
        } catch (EmptyResultDataAccessException dex) {
            return null;
        } catch (DataAccessException dex) {
            throw new ServiceException("查询失败", dex);
        }
    }
    @Override
    public <T> T queryOneReocrd(String sql, List<Object> args, int[] argTypes, RowMapper<T> rowMapper) {
        try {
            return (T) this.jdbcTemplate.queryForObject(sql, args != null ? args.toArray() : null, argTypes, rowMapper);
        } catch (EmptyResultDataAccessException dex) {
            return null;
        } catch (DataAccessException dex) {
            throw new ServiceException("查询失败", dex);
        }
    }
    @Override
    public int update(String sql) {
        try {
            return this.jdbcTemplate.update(sql);
        } catch (DataAccessException dex) {
            throw new ServiceException("更新失败", dex);
        }
    }
    @Override
    public int updateJdbc(String sql, Object... args) {
        try {
            return this.jdbcTemplate.update(sql, args);
        } catch (DataAccessException dex) {
            throw new ServiceException("更新失败", dex);
        }
    }
    @Override
    public int update(String sql, List<Object> args) {
        try {
            return this.jdbcTemplate.update(sql, args != null ? args.toArray() : null);
        } catch (DataAccessException dex) {
            throw new ServiceException("更新失败", dex);
        }
    }
    @Override
    public int update(String sql, List<Object> args, int[] argTypes) {
        try {
            return this.jdbcTemplate.update(sql, args != null ? args.toArray() : null, argTypes);
        } catch (DataAccessException dex) {
            throw new ServiceException("更新失败", dex);
        }
    }
    @Override
    public Map<String, Object> call(String name, boolean isFunction, Map<String, Object> procedureParamMap, List<SqlParameter> sqlInParameters, List<SqlOutParameter> sqlOutParameter) {
        try {
            StoredProcedure storedProcedure = new StoredProcedureHandler(this.jdbcTemplate, name);
            if ((sqlInParameters != null) && (!sqlInParameters.isEmpty())) {
                for (SqlParameter sqlParameter : sqlInParameters) {
                    storedProcedure.declareParameter(sqlParameter);
                }
            }
            if ((sqlOutParameter != null) && (!sqlOutParameter.isEmpty())) {
                for (SqlOutParameter sqlParameter : sqlOutParameter) {
                    storedProcedure.declareParameter(sqlParameter);
                }
            }
            storedProcedure.setFunction(isFunction);
            storedProcedure.compile();
            return storedProcedure.execute(procedureParamMap);
        } catch (DataAccessException dex) {
            throw new ServiceException("存过执行失败", dex);
        }
    }
    @Override
    public Map<String, Object> call(String name, boolean isFunction, Map<String, Object> procedureParamMap, List<SqlParameter> sqlParameters)
            throws ServiceException {
        try {
            StoredProcedure storedProcedure = new StoredProcedureHandler(this.jdbcTemplate, name);
            if ((sqlParameters != null) && (!sqlParameters.isEmpty())) {
                for (SqlParameter sqlParameter : sqlParameters) {
                    storedProcedure.declareParameter(sqlParameter);
                }
            }
            storedProcedure.setFunction(isFunction);
            storedProcedure.compile();
            return storedProcedure.execute(procedureParamMap);
        } catch (DataAccessException dex) {
            throw new ServiceException("存过执行失败", dex);
        }
    }
    @Override
    public <E> List<E> selectList(String statementName, Object param, int offset, int limit)
            throws ServiceException {
        try {
            if (limit < 0) {
                return selectList(statementName, param);
            }
            return this.session.selectList(statementName, param, new RowBounds(offset, limit));
        } catch (Exception e) {
            if (this.fallbackExceptions != null) {
                for (Class<Throwable> c : this.fallbackExceptions) {
                    if (c.isAssignableFrom(e.getClass())) {
                        return this.fallback.selectList(statementName, param, offset, limit);
                    }
                }
            }
            throw new ServiceException("查询失败", e);
        }
    }
    @Override
    public <E> List<E> selectList(String statementName, Object param)
            throws ServiceException {
        try {
            return this.session.selectList(statementName, param);
        } catch (Exception e) {
            if (this.fallbackExceptions != null) {
                for (Class<Throwable> c : this.fallbackExceptions) {
                    if (c.isAssignableFrom(e.getClass())) {
                        return this.fallback.selectList(statementName, param);
                    }
                }
            }
            throw new ServiceException("查询失败", e);
        }
    }
    @Override
    public <T> T selectOne(String statementName, Object param)
            throws ServiceException {
        try {
            return (T) this.session.selectOne(statementName, param);
        } catch (Exception e) {
            if (this.fallbackExceptions != null) {
                for (Class<Throwable> c : this.fallbackExceptions) {
                    if (c.isAssignableFrom(e.getClass())) {
                        return (T) this.fallback.selectOne(statementName, param);
                    }
                }
            }
            throw new ServiceException("查询失败", e);
        }
    }
    @Override
    public Object[] getCommonPaged(String queryStatement, String countStatement, Object param, int offset, int limit)
            throws ServiceException {
        try {
            Object[] obj = new Object[2];
            obj[0] = selectList(queryStatement, param, offset, limit);
            obj[1] = selectOne(countStatement, param);
            return obj;
        } catch (Exception e) {
            if (this.fallbackExceptions != null) {
                for (Class<Throwable> c : this.fallbackExceptions) {
                    if (c.isAssignableFrom(e.getClass())) {
                        return this.fallback.getCommonPaged(queryStatement, countStatement, param, offset, limit);
                    }
                }
            }
            throw new ServiceException("查询失败", e);
        }
    }


    @Override
    public int[] batchUpdate(String... sql)
            throws ServiceException {
        try {
            return this.jdbcTemplate.batchUpdate(sql);
        } catch (Exception e) {
            throw new ServiceException("批量更新失败", e);
        }
    }
    @Override
    public void batchUpdate(String sql, List<Object[]> batchArgs)
            throws ServiceException {
        try {
            int fromIndex = 0;
            int toIndex = 5000;
            while (fromIndex < batchArgs.size()) {
                if (toIndex > batchArgs.size()) {
                    toIndex = batchArgs.size();
                }
                this.jdbcTemplate.batchUpdate(sql, batchArgs.subList(fromIndex, toIndex));
                fromIndex = toIndex;
                toIndex += 5000;
            }
        } catch (Exception e) {
            throw new ServiceException("批量更新失败", e);
        }
    }
    @Override
    public void batchUpdate(String sql, BatchPreparedStatementSetter pss)
            throws ServiceException {
        try {
            this.jdbcTemplate.batchUpdate(sql, pss);
        } catch (Exception e) {
            throw new ServiceException("批量更新失败", e);
        }
    }
    @Override
    public <T> void batchUpdate(String sql, Collection<T> batchArgs, int batchSize, ParameterizedPreparedStatementSetter<T> ppss)
            throws ServiceException {
        try {
            this.jdbcTemplate.batchUpdate(sql, batchArgs, batchSize, ppss);
        } catch (Exception e) {
            throw new ServiceException("批量更新失败", e);
        }
    }
    @Override
    public String keyCreate(String sequence, Character prefix)
            throws ServiceException {
        try {
            Long seqId = (Long) this.jdbcTemplate.queryForObject("select " + sequence + ".nextval from dual", Long.class);
            return format20bit(prefix, seqId.longValue());
        } catch (Exception e) {
            throw new ServiceException("seqId创建失败", e);
        }
    }
    @Override
    public String keyCreatePad15Bit(String sequence, Character prefix)
            throws ServiceException {
        try {
            Long seqId = (Long) this.jdbcTemplate.queryForObject("select " + sequence + ".nextval from dual", Long.class);
            return format15bit(prefix, seqId.longValue());
        } catch (Exception e) {
            throw new ServiceException("seqId创建失败", e);
        }
    }

    private String format(long initval, long number) {
        return String.valueOf(initval + number).substring(1);
    }

    public static final FastDateFormat df = FastDateFormat.getInstance("yyyyMMdd");

    private String format20bit(Character prefix, long number) {
        return getIdBySeq(prefix == null ? "" : prefix.toString(), String.valueOf(number));
    }

    public static String getIdBySeq(String seqId) {
        StringBuffer seqBuf = new StringBuffer();
        if ((seqId != null) && (seqId.length() > 0) && (seqId.length() < 13)) {
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
            seqBuf.append(df.format(new Date()));
            for (int i = 0; i < 12 - seqId.length(); i++) {
                seqBuf.append("0");
            }
            seqBuf.append(seqId);
        } else if ((seqId != null) && (seqId.length() > 12)) {
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
            seqBuf.append(df.format(new Date()));
            seqBuf.append(seqId.substring(seqId.length() - 12));
        }
        return seqBuf.toString();
    }

    public static String getIdBySeq(String prefix, String seqId) {
        StringBuffer seqBuf = new StringBuffer(prefix);
        if ((seqId != null) && (seqId.length() > 0) && (seqId.length() + prefix.length() < 13)) {
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
            seqBuf.append(df.format(new Date()));
            for (int i = 0; i < 12 - prefix.length() - seqId.length(); i++) {
                seqBuf.append("0");
            }
            seqBuf.append(seqId);
        } else if ((seqId != null) && (seqId.length() + prefix.length() > 12)) {
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
            seqBuf.append(df.format(new Date()));
            seqBuf.append(seqId.substring(seqId.length() + prefix.length() - 12));
        }
        return seqBuf.toString();
    }

    private String format15bit(Character prefix, long number) {
        number %= (prefix == null ? 10000000L : 1000000L);
        String num = format(prefix == null ? 10000000L : 1000000L, number);
        String datestr = df.format(new Date());
        return (prefix == null ? "" : prefix) + datestr + num;
    }
    @Override
    public long getSeqByName(String sequence)
            throws ServiceException {
        try {
            return ((Long) this.jdbcTemplate.queryForObject("select " + sequence + ".nextval from dual", Long.class)).longValue();
        } catch (Exception e) {
            throw new ServiceException("seqId创建失败", e);
        }
    }
    @Override
    public String getIdBySeq(Character prefix, long number)
            throws ServiceException {
        try {
            return format20bit(prefix, number);
        } catch (Exception e) {
            throw new ServiceException("seqId创建失败", e);
        }
    }
}

